﻿// (c) Copyright 2013 Mike Riedel.
// This source is subject to the Microsoft Public License (Ms-PL).
// All other rights reserved.

using System;
using System.Threading.Tasks;
using FreshMeat.ErrorHandlingBlock;
using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace ErrorHandlingBlock.Tests
{
   [TestClass]
   public class ErrorHandlerTest
   {
      /// <summary>
      /// An empty handler should simply pass along the original exception.
      /// </summary>
      [TestMethod]
      public async Task DefaultTest()
      {
         var result0 = new FakeException1();
         var mock = new MockTarget(result0);
         var handler = new ErrorHandler();

         try
         {
            await handler.Run(mock.Action);

            Assert.Fail("Should have thrown an exception.");
         }
         catch (Exception e)
         {
            Assert.AreSame(e, result0, "Thrown instance is wrong.");
            Assert.AreEqual(1, mock.ActionCount, "Action should have been called once.");
         }
      }

      /// <summary>
      /// A handler that allows 1 attempt should throw on the 1st error.
      /// </summary>
      [TestMethod]
      public async Task FailOnAttemptOneOfOneTest()
      {
         var error = new FakeException1();
         var mock = new MockTarget(error);
         var handler = new ErrorHandler(new ErrorPolicy(new FakeDetector(), new FixedRetryPlan(1, TimeSpan.Zero)));

         try
         {
            await handler.Run(mock.Action);

            Assert.Fail("Should have thrown an exception.");
         }
         catch (Exception e)
         {
            Assert.AreSame(e, error, "Should be original instance.");
            Assert.AreEqual(1, mock.ActionCount, "Should have called it once.");
         }
      }

      /// <summary>
      /// A handler that allows 2 attempts should not throw if only 1 error occurs.
      /// </summary>
      [TestMethod]
      public async Task PassAfterRetry()
      {
         var error = new FakeException1();
         var mock = new MockTarget(error, null); // throw first then succeed
         var detector = new FakeDetector { RelevantType = typeof(FakeException1) };
         var retryPlan = new FakeRetryPlan(2);
         var handler = new ErrorHandler(new ErrorPolicy(detector, retryPlan));

         try
         {
            await handler.Run(mock.Action);

            Assert.AreEqual(2, mock.ActionCount, "Should have succeeded on second try.");
         }
         catch (Exception)
         {
            Assert.Fail("Should not have thrown an exception.");
         }
      }

      /// <summary>
      /// A handler that allows 2 attempts should throw on the 2nd error.
      /// </summary>
      [TestMethod]
      public async Task FailAfterRetry()
      {
         var error = new FakeException1();
         var mock = new MockTarget(error, error); // throw twice in a row
         var detector = new FakeDetector { RelevantType = typeof(FakeException1)};
         var retryPlan = new FakeRetryPlan(2);
         var handler = new ErrorHandler(new ErrorPolicy(detector, retryPlan));

         try
         {
            await handler.Run(mock.Action);

            Assert.Fail("Should have thrown an exception.");
         }
         catch (Exception e)
         {
            Assert.AreSame(e, error, "Should be original instance.");
            Assert.AreEqual(retryPlan.MaxAttempts, mock.ActionCount, "Should have called action number of times specified by plan.");
         }
      }
   }
}
